namespace FluentDataContract
{
    using System;
    using System.Collections.Generic;
    using System.Linq.Expressions;
    using System.Reflection;

    public sealed class PropertyBuilder<TObject> : IPropertyBuilder<TObject>
    {
        private readonly IObjectBuilder<TObject> _parentObjectBuilder;
        public IObjectBuilder<TObject> ParentObjectBuilder { get { return _parentObjectBuilder; } }

        private readonly IObjectContract _parentObjectContract;
        public IObjectContract ParentObjectContract { get { return _parentObjectContract; } }

        private List<IPropertyContract> _contracts;
        public List<IPropertyContract> Contracts { get { return _contracts ?? (_contracts = new List<IPropertyContract>()); } }

        public PropertyBuilder(IObjectBuilder<TObject> parentObjectBuilder, MemberInfo memberInfo)
        {
            _parentObjectBuilder = parentObjectBuilder;
            _parentObjectContract = parentObjectBuilder.Contract;

            Contracts.Add(new PropertyContract
            {
                    Name = memberInfo.Name,
                    PropertyName = memberInfo.Name,
                    Type = memberInfo.DeclaringType,
                    IncludeDefaultValues = ParentObjectBuilder.Contract.IncludeDefaultValues,
                    IncludeNullValues = ParentObjectBuilder.Contract.IncludeNullValues
            });
        }

        public PropertyBuilder(IObjectBuilder<TObject> parentObjectBuilder, params MemberInfo[] members)
        {
            _parentObjectBuilder = parentObjectBuilder;
            _parentObjectContract = parentObjectBuilder.Contract;

            foreach(var member in members)
                Contracts.Add(new PropertyContract {Name = member.Name, PropertyName = member.Name, Type = member.DeclaringType});
        }

        public IPropertyBuilder<TObject> IncludeDefaultValues()
        {
            foreach(var contract in Contracts)
                contract.IncludeDefaultValues = true;

            return this;
        }

        public IPropertyBuilder<TObject> ExcludeDefaultValues()
        {
            foreach (var contract in Contracts)
                contract.IncludeDefaultValues = false;

            return this;
        }

        public IPropertyBuilder<TObject> IncludeNullValues()
        {
            foreach (var contract in Contracts)
                contract.IncludeNullValues = true;

            return this;
        }

        public IPropertyBuilder<TObject> ExcludeNullValues()
        {
            foreach (var contract in Contracts)
                contract.IncludeNullValues = false;

            return this;
        }

        public IPropertyBuilder<TObject> IsReference()
        {
            foreach (var contract in Contracts)
                contract.IsReference = true;

            return this;
        }

        public IPropertyBuilder<TObject> ReferenceLoopHanding(ReferenceLoopHandling referenceLoopHandling)
        {
            foreach (var contract in Contracts)
                contract.ReferenceLoopHandling = referenceLoopHandling;

            return this;
        }

        public IPropertyBuilder<TObject> Order(int order)
        {
            foreach (var contract in Contracts)
                contract.Order = order;

            return this;
        }

        // TODO
        public IPropertyBuilder<TObject> Item()
        {
            throw new NotImplementedException();
        }

        public IPropertyBuilder<TObject> Property<TProperty>(Expression<Func<TObject, TProperty>> expression)
        {
            return ParentObjectBuilder.Property(expression);
        }

        public IPropertyBuilder<TObject> Properties(params Expression<Func<TObject, object>>[] expressions)
        {
            return ParentObjectBuilder.Properties(expressions);
        }

        public IPropertyBuilder<TObject> IncludeProperty<TProperty>(Expression<Func<TObject, TProperty>> expression)
        {
            return ParentObjectBuilder.IncludeProperty(expression);
        }

        public IPropertyBuilder<TObject> IncludeProperties(params Expression<Func<TObject, object>>[] expressions)
        {
            return ParentObjectBuilder.IncludeProperties(expressions);
        }

        public IObjectBuilder<TObject> ExcludeProperty<TProperty>(Expression<Func<TObject, TProperty>> expression)
        {
            return ParentObjectBuilder.ExcludeProperty(expression);
        }

        public IObjectBuilder<TObject> ExcludeProperties(params Expression<Func<TObject, object>>[] expressions)
        {
            return ParentObjectBuilder.ExcludeProperties(expressions);
        }
    }
}